package com.example.rabbitmq.controller;

import com.example.rabbitmq.config.RabbitMQConfiguration;
import com.example.rabbitmq.pojo.TestPojo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.servlet.ServletException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/*
如果报这种错误，是因为Restful风格需要用@RestController，而不能用@Controller
would dispatch back to the current handler URL [/asdf] again. Check your ViewResolver setup
 */
@RestController
public class TestController {



    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 给RabbitTemplate添加发布确认的监听器、消息回退ReturnCallback监听器
     */
    @PostConstruct
    public void init(){
        //相当于Mandatory,在配置文件里设置
//        rabbitTemplate.setMandatory(true);
        /**
         * 路由不到队列，则会消息回退
         * message：回退的消息
         * replyCode：回退的状态码
         * replyText：触发回退的原因
         * exchange：发送的交换机
         * routingKey：发送的routingKey
         */
        //为RabbitTemplate添加ReturnCallback
        //ReturnCallback属性相当于发布确认的ReturnCallback接口
        rabbitTemplate.setReturnCallback((message,replyCode,replyText,exchange,routingKey)->{
            System.out.printf("回退的消息:%s \t状态码：%d \t原因：%s \t交换机名：%s \t路由键：%s\n",new String(message.getBody(),StandardCharsets.UTF_8),replyCode,replyText,exchange,routingKey);
        });
        /**
         * 交换机到达就触发，交换机不到达也触发
         * correlationData: 消费者消费监听器、消费后的消息的相关信息的获取与修改
         * ack:发布是否成功，即是否到达交换机，到达则返回true，否则false
         * cause：发布失败的原因，如果成功，则为null
         */
        //为RabbitTemplate添加ConfirmCallback发布确认监听器
        //ConfirmCallback属性相当于发布确认的ConfirmCallback接口
        rabbitTemplate.setConfirmCallback((correlationData,ack,cause)->{
            Message returnedMessage = correlationData!=null?correlationData.getReturnedMessage():null;
            String msg=returnedMessage!=null?new String(returnedMessage.getBody(), StandardCharsets.UTF_8):"";
            if(!ack){
                System.out.printf("发布的消息：%s \t发布状态：false \t失败原因：%s\n",msg,cause);
            }else{
                System.out.printf("发布的消息：%s \t发布状态：true \t失败原因：%s\n",msg,cause);
            }
        });


    }


//################################### 测试 #############################################
    /**
     * 测试消息转换
     * 发布时，将java对象发送出去，发布时，会将java对象转换成message消息对象
     * 消费者接收到时，将message消息对象转换成java对象
     */
    @RequestMapping("/MessageConverter")
    public void testMessageConverter()  {
        //消息发送前的消息处理
        MessagePostProcessor messagePostProcessor=(msg)->{
            //这个其实就是将basicPublish方法的方法参数
            MessageProperties messageProperties = msg.getMessageProperties();
            messageProperties.setConsumerTag("ConsumerTag");
            //设置消息类型，默认application/octet-stream
            //CONTENT_TYPE_SERIALIZED_OBJECT:application/x-java-serialized-object
            //CONTENT_TYPE_JSON：application/json
            //设置为json字符串类型
            messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
            //消息是否持久化
            //messageProperties.setReceivedDeliveryMode(MessageDeliveryMode.PERSISTENT);
            return msg;
        };
        CorrelationData correlationData=new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend(RabbitMQConfiguration.DIRECT_EXCHANGE_NAME2,RabbitMQConfiguration.ROUTING_KEY,new TestPojo("名字",123), messagePostProcessor,correlationData);
    }


    /**
     * 发布确认、消息回退、发布参数设置
     * @param message
     */
    @RequestMapping("/publishAck/{message}")
    public void testPublishAck(@PathVariable String message){
        //消息发送前的消息处理
        MessagePostProcessor messagePostProcessor=(msg)->{
            //这个其实就是将basicPublish方法的方法参数
            MessageProperties messageProperties = msg.getMessageProperties();
            messageProperties.setConsumerTag("ConsumerTag");
            //消息是否持久化
            messageProperties.setReceivedDeliveryMode(MessageDeliveryMode.PERSISTENT);
            return msg;
        };
        CorrelationData correlationData=new CorrelationData(UUID.randomUUID().toString());
        //send相当于basicPublish方法
        message+=new Date();
        //插件的延迟队列，会百分百引发returnMessage方法回调
        //只要交换机接受到就触发，接受不到生产者的消息，发布确认成功
        rabbitTemplate.convertAndSend(RabbitMQConfiguration.DIRECT_EXCHANGE_NAME2,RabbitMQConfiguration.ROUTING_KEY,message, messagePostProcessor,correlationData);
        //消息发布确认失败，接受不到生产者的消息
        //rabbitTemplate.convertAndSend("RabbitMQConfiguration.DIRECT_EXCHANGE_NAME2",RabbitMQConfiguration.ROUTING_KEY,message, messagePostProcessor,correlationData);
        //消息被回退
//        rabbitTemplate.convertAndSend(RabbitMQConfiguration.DIRECT_EXCHANGE_NAME2,"RabbitMQConfiguration.ROUTING_KEY",message, messagePostProcessor,correlationData);

    }

    /**
     * springboot的ReturnCallback消息回退对延迟队列的百分百触发、延迟交换机
     * @param delayTime
     * @param message
     */
    @RequestMapping("/basePlugsDelayMessage/{delayTime}/{message}")
    public void testBasePlugsDelayMessage(@PathVariable Integer delayTime,@PathVariable String message){
        //消息发送前的消息处理
        MessagePostProcessor messagePostProcessor=(msg)->{
            //这个其实就是将basicPublish方法的方法参数
            MessageProperties messageProperties = msg.getMessageProperties();
            //基于插件的延迟时间，即该消息是在交换机转发给对应的队列之前进行的计时，即到时间才转发到队列
            messageProperties.setDelay(delayTime);
            //基于消息队列的过期时间，即该消息是在消息队列里进行计时
            //messageProperties.setExpiration();
            messageProperties.setConsumerTag("ConsumerTag");
            //messageProperties.setDeliveryTag(deliveryTag的唯一标识，确保唯一);
            //消息是否持久化
            messageProperties.setReceivedDeliveryMode(MessageDeliveryMode.PERSISTENT);
            //消息的优先级
            //messageProperties.setPriority(1);
            return msg;
        };
        CorrelationData correlationData=new CorrelationData(UUID.randomUUID().toString());
        //send相当于basicPublish方法
        message+=new Date();
        //插件的延迟队列，会引发returnMessage方法回调
        rabbitTemplate.convertAndSend(RabbitMQConfiguration.DELAY_EXCHANGE_NAME,RabbitMQConfiguration.ROUTING_KEY,message, messagePostProcessor,correlationData);
    }

    /**
     *
     */
    @RequestMapping("/backupExchange/{message}")
    public void testBackupExchange(@PathVariable String message){
        //消息发送前的消息处理
        MessagePostProcessor messagePostProcessor=(msg)->{
            //这个其实就是将basicPublish方法的方法参数
            MessageProperties messageProperties = msg.getMessageProperties();
            messageProperties.setConsumerTag("ConsumerTag");

            //消息是否持久化
            messageProperties.setReceivedDeliveryMode(MessageDeliveryMode.PERSISTENT);
            return msg;
        };
        CorrelationData correlationData=new CorrelationData(UUID.randomUUID().toString());
        //send相当于basicPublish方法
        message+=new Date();
        //由于键对应的队列不存在，则会转发给备用队列,看看备用队列是否有对应的键
        //由于备用队列是fanout，所以是百分百可以接收的。
        rabbitTemplate.convertAndSend(RabbitMQConfiguration.DIRECT_EXCHANGE_NAME,"RabbitMQConfiguration.ROUTING_KEY",message, messagePostProcessor,correlationData);

    }
}
